#!/usr/bin/env python
# -*- coding: utf-8 -*-
#=======================================#
# @author: RedSoxFan and NerfSoldier
# @copyright: 2012 RedSoxFan and NerfSoldier
# @license: GNU General Public License v3.0 
#           <http://www.gnu.org/copyleft/gpl.html>
# @summary: PlainText (Base) Parser
#=======================================#

#=======================================#
# Imports
#=======================================#
# Python
from PyQt4 import QtGui, QtCore
import os
# DarkDev
#=======================================#
class Parser(QtGui.QSyntaxHighlighter):
    description = "Plain Text"
    filefilter = "*.*"
    key = "Default"
    def __init__(self,project,sv,fname=None,ddcg=None):
        QtGui.QSyntaxHighlighter.__init__(self,sv.document())
        self.project = project
        self.workspace = project.workspace
        self.darkdev = project.darkdev
        self.qc = None
        self.refresh(self.darkdev)
        self.sv = sv
        self.fname = fname
        self.create() 
        self.extras = []
        self.rev = ""
        self.frame = DocumentationPopup(self.sv)
        self.frame.setReadOnly(True)
        self.frame.hide()
        if fname and os.path.exists(fname):
            self.fname = os.path.abspath(self.fname)
            if ddcg: self.load(self.fname,ddcg)
            else: self.load(self.fname)
        self.tokens = []
        self.stylesheet = {}
        if "Default.Editor" in self.darkdev.ddcg:
            k = self.darkdev.ddcg["Default.Editor"]
            self.load_key(k)
        if self.key!="Default":
            if "%s.Editor"%self.key in self.darkdev.ddcg:
                k = self.darkdev.ddcg["%s.Editor"%self.key]
                self.load_key(k)
        self.sv.setStyleSheet("\n".join("%s: %s;"%(k,v) for k,v in self.stylesheet.iteritems()))
    @classmethod
    def init(cls,darkdev):
        darkdev.logger.debug("%s Initialized Successfully"%cls.description)
        return True
    @classmethod
    def deinit(cls,darkdev):
        darkdev.logger.debug("%s Deinitialized Successfully"%cls.description)
        return True
    def load_key(self,k):
        if "Foreground" in k:
            self.stylesheet["color"]=k["Foreground"]
            self.sv.ln.setStyleSheet("color: #%s"%"".join("%02x"%i for i in QtGui.QColor(k["Foreground"]).darker().getRgb()[:3]))
        if "Background" in k:
            self.stylesheet["background"]=k["Background"]
        if "Font" in k:
            self.stylesheet["font"]=k["Font"]
        if "TabWidth" in k:
            self.sv.setTabStopWidth(int(k["TabWidth"]))
    @classmethod
    def load_format(cls,darkdev,cat):
        dd = darkdev.ddcg
        fmt = QtGui.QTextCharFormat()
        if "%s.Theme.%s"%(cls.key,cat) in dd:
            k = dd["%s.Theme.%s"%(cls.key,cat)]
            if "Foreground" in k:
                fmt.setForeground(QtGui.QBrush(QtGui.QColor(k["Foreground"])))
            if "Background" in k:
                fmt.setBackground(QtGui.QBrush(QtGui.QColor(k["Background"])))
            if "Italics" in k:
                fmt.setFontItalic(k["Italics"])
            if "Weight" in k:
                fmt.setFontWeight(int(k["Weight"]))
        return fmt
    def create(self):
        pass
    def load(self,fname):
        f = open(fname,"r")
        self.sv.setText(f.read())
        f.close()
        del f
        self.rev = self.sv.toPlainText()
    def revert(self):
        self.sv.setText(self.rev)
        self.setClean(self.fname)
    def saveas(self):
        fd = QtGui.QFileDialog()
        fname = fd.getSaveFileName(self.darkdev,'Save As',self.fname,self.filefilter)
        if fname: 
            self.save(fname)
    def save(self,fname=None):
        if not fname: o=True; fname=self.fname
        else: o = False
        if fname:
            f = open(fname,"w")
            f.write(self.sv.toPlainText())
            f.flush()
            f.close()
            del f
        if o:
            self.setClean(fname)
            self.rev = self.sv.toPlainText()
        del o
    def setClean(self,fname):
        self.sv.set_tab_text("%s"%os.path.basename(fname))
    @classmethod
    def refresh(cls,darkdev):
        cls.style = {}
        for cat in darkdev.ddcg["%s.Theme"%cls.key]:
            cls.style[cat] = cls.load_format(darkdev,cat)
    def validate(self):
        pass
    def highlightBlock(self,text):
        pass
    
class DocumentationPopup(QtGui.QTextEdit):
    def setCodeCompletionPopup(self,qc):
        self.qc = qc
    def setText(self, text):
        QtGui.QTextEdit.setText(self,"")
        self.setFixedWidth(1)
        return QtGui.QTextEdit.setText(self, text)
    def hideEvent(self, e):
        self.setText("")
        self.setFixedWidth(1) 
        return QtGui.QTextEdit.hideEvent(self, e)
    def showEvent(self,e):
        if not self.qc.isVisible(): self.hide()
    def focusInEvent(self, *args, **kwargs):
        self.qc.setFocus()
    
class CodeCompletionPopup(QtGui.QListWidget):
    ignoreNewLine = False
    highlighted = QtCore.pyqtSignal(str)
    triggered = QtCore.pyqtSignal(str)
    def __init__(self,parent,listitems,prefix=""):
        QtGui.QListView.__init__(self,parent)
        for listitem in listitems: 
            if listitem.strip(): self.addItem(str(listitem))
        self.itemActivated.connect(lambda i:self.triggered.emit(str(i.text())) or self.hide())
        self.currentRowChanged.connect(lambda i:self.highlighted.emit(str(self.item(i).text())))
        self.htmldelegate = HTMLDelegate(parent)
        self.setItemDelegate(self.htmldelegate)
        self.prefix=prefix
    def complete(self,qp):
        self.move(qp)
        self.setFixedWidth(self.sizeHintForColumn(0)+self.verticalScrollBar().sizeHint().width()+5)
        self.show()
        h = 5
        for x in range(self.count()): h+=self.sizeHintForRow(x) 
        if h<self.height():
            self.setFixedHeight(h)
    def keyPressEvent(self,e):
        if e.key() not in [QtCore.Qt.Key_Enter,QtCore.Qt.Key_Return,QtCore.Qt.Key_Up,QtCore.Qt.Key_Down]:
            self.hide()
            return self.parentWidget().keyPressEvent(e)
        if e.key()==QtCore.Qt.Key_Down and self.currentRow()==self.count()-1:
            self.setCurrentRow(0)
            e.ignore()
            return
        elif e.key()==QtCore.Qt.Key_Up and self.currentRow()==0:
            self.setCurrentRow(self.count()-1)
            e.ignore()
            return
        elif e.key() in [QtCore.Qt.Key_Enter,QtCore.Qt.Key_Return]:
            CodeCompletionPopup.ignoreNewLine=True
            self.triggered.emit(str(self.item(self.currentRow()).text()))
            e.ignore()
            return
        return QtGui.QListWidget.keyPressEvent(self,e)
    def hideEvent(self,e):
        self.parentWidget().setFocus()
        
class HTMLDelegate(QtGui.QStyledItemDelegate):
    '''
    Credit: jbmohler on StackOverflow 
    '''
    def paint(self, painter, option, index):
        options = QtGui.QStyleOptionViewItemV4(option)
        self.initStyleOption(options,index)
        style = QtGui.QApplication.style() if options.widget is None else options.widget.style()
        doc = QtGui.QTextDocument()
        doc.setHtml(options.text)
        doc.setTextWidth(options.rect.width())
        options.text = ""
        style.drawControl(QtGui.QStyle.CE_ItemViewItem, options, painter);
        ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
        textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, options)
        painter.save()
        painter.translate(textRect.topLeft())
        painter.setClipRect(textRect.translated(-textRect.topLeft()))
        doc.documentLayout().draw(painter, ctx)
        painter.restore()
    def sizeHint(self, option, index):
        options = QtGui.QStyleOptionViewItemV4(option)
        self.initStyleOption(options,index)
        doc = QtGui.QTextDocument()
        doc.setHtml(options.text)
        return QtCore.QSize(doc.size().width(), doc.size().height())